/*
 * Copyright (c) 2007, Sun Microsystems, Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * 
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of Sun Microsystems, Inc. nor the names of its contributors
 *   may be used to endorse or promote products derived from this software without
 *   specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */
package authenticate;

import Game.bc.BullCow;
import Game.bc.GameLogicRemote;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.faces.application.FacesMessage;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.servlet.http.HttpSession;
import javax.transaction.UserTransaction;

/**
 * <p>A simple managed bean to mediate between the user
 * and the persistence layer.</p>
 * @author rlubke
 */
@SessionScoped
public class UserManager {

    /**
     * <p>The key for the session scoped attribute holding the
     * appropriate <code>Player</code> instance.</p>
     */
    public static final String USER_SESSION_KEY = "user";
    /**
     * <p>The <code>PersistenceContext</code>.</p>
     */
    @PersistenceContext
    private EntityManager em;
    /**
     * <p>The transaction resource.</p>
     */
    @Resource
    private UserTransaction utx;
    /**
     * <p>User properties.</p>
     */
    private String username;
    private String password;
    private String passwordv;
    private String fname;
    private String lname;
    private GameLogicRemote gameLogic = lookupGameLogicRemote();


    String playerName = "Name";
    Game.bc.Player player = null;
    private String state = "GETNAME"; // GETNAME , GETGAME , READY
    int wordLength = 4;
    String response;
    List<String> list = null;
    String gameListResponse;
    String creatorName;
    String guessedWord = new String();
    StringBuffer pastGuesses = new StringBuffer();
    int guessNo = 1;

    public List<String> getList() {
        list = gameLogic.getGameList();
        return list;
    }

    public void setList(List<String> list) {

        this.list = gameLogic.getGameList();
    }

    public int getGuessNo() {
        System.out.println("*******: getGuessNo");
        return guessNo;
    }

    public String getCreatorName() {
        System.out.println("*******: getCreatorName");
        return creatorName;
    }

    public void setCreatorName(String creatorName) {
        this.creatorName = creatorName;
        System.out.println("*******: setCreatorName");


    }

    public int getWordLength() {
        System.out.println("*******: getWordLength");
        return wordLength;
    }

    public void setWordLength(int wordLength) {
        System.out.println("*******: setPlayerName");
        state = "GETGAME";


        System.out.println("*******: setWordLength");
        state = "READY";
        guessNo = 0;
        gameLogic.createGame(wordLength, player);
        this.wordLength = wordLength;
    }

    public String getGameListResponse() {
        System.out.println("*******: getGameListResponse");
        StringBuffer s = new StringBuffer();
        ArrayList<String> gl = gameLogic.getGameList();
        Iterator<String> itr = gl.iterator();
        while (itr.hasNext()) {
            String element = itr.next();
            s.append(element);
        }
        return s.toString();
    }

    public void setGameListResponse(String gameListResponse) {
        System.out.println("*******: setGameListResponse");
        this.gameListResponse = gameListResponse;
    }

    public String getPlayerName() {
        System.out.println("*******: getPlayerName");
        return playerName;
    }

    public String getGuessedWord() {
        System.out.println("*******: getGuessedWord");
        return guessedWord;///
    }

    public void setGuessedWord(String guessedWord) {
        System.out.println("*******: setGuessedWord");
        this.guessedWord = guessedWord;
    }

    public String getResponse() {
        System.out.println("********getResponse User :" + username);
        System.out.println("*******: getResponse");
        int time = gameLogic.getTimerSeconds();
        gameLogic.resetTimerSeconds();
        System.out.println("State " + state);
        if (state.equals("READY") && !guessedWord.trim().equals("")) {
            guessNo++;
            System.out.println("theWord:" + gameLogic.getTheWord());
            System.out.println("guessedWord:" + guessedWord);
            BullCow bc = gameLogic.makeGuess(guessedWord, player);
            int bull = bc.getBull();
            int cow = bc.getCow();
            System.out.println("Seconds after last guess: " + time);
            if (bull == wordLength) {
                String actualWord = gameLogic.getTheWord();
                String temp = response;
                initialize();
                setWordLength(wordLength);
                return (temp + "</table> <br/>  Bull: " + wordLength + " Cow: 0 <br/> YOU WIN! <br/> The word was " + actualWord);
            } else if (bull == 9 && cow == 9) {
                response = "Invalid Guess! ";
                pastGuesses.append(response + "<br/>");
                guessedWord = gameLogic.call(wordLength);
            } else {
                if (time <= 1) {
                    response = gameLogic.getOutput() + "</table> <br/> Time since last guess:" + time + " second.";
                } else {
                    response = gameLogic.getOutput() + "</table> <br/> Time since last guess: " + time + " seconds.";
                }

                //  System.out.println("PRINTING RESPONSE:" +response);
                //  response = "&#9;Bull :&#9;" + bull + "&#9;Cow :&#9;" + cow;
                //  pastGuesses.append(guessedWord + "  " + response + "<br/>");
                guessedWord = gameLogic.call(wordLength);
            }
            return response;
        } else if (state.equals("GETGAME")) {
            System.out.println("get Game " + playerName);
            return "Game Created";
        } else {
            System.out.println("Welcome To Bulls And Cows <br/> You Have joined as " + this.username);
            return "Welcome To Bulls And Cows <br/> You Have joined as " + this.username;
        }
    }

    public String joinGame() {
        guessNo = 0;
        if (gameLogic.joinGame(creatorName)) {
            state = "READY";
            return "gamesession";
        } else {
            gameLogic.createGame(4, player);
            state = "READY";
            System.out.println("*******: Game doesnot exit");
            return "gamesession";
        }
    }

    public void endSession() {
        System.out.println("*******: endSession");
        //invalidate user session
        FacesContext context = FacesContext.getCurrentInstance();
        HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
        session.invalidate();
    }

    public void initialize() {
        System.out.println("*******: initialize");
        response = "";
        guessedWord = new String();
        pastGuesses = new StringBuffer();
        guessNo = 0;
    }

    private GameLogicRemote lookupGameLogicRemote() {
        System.out.println("*******: lookupGameLogicRemote");
        try {
            Context c = new InitialContext();
            return (GameLogicRemote) c.lookup("java:global/bullsandcows/bullsandcows-ejb/GameLogic!Game.bc.GameLogicRemote");
        } catch (NamingException ne) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "exception caught", ne);
            throw new RuntimeException(ne);
        }
    }

    // -------------------------------------------------------------- Properties
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPasswordv() {
        return passwordv;
    }

    public void setPasswordv(String passwordv) {
        this.passwordv = passwordv;
    }

    public String getFname() {
        return fname;
    }

    public void setFname(String fname) {
        this.fname = fname;
    }

    public String getLname() {
        return lname;
    }

    public void setLname(String lname) {
        this.lname = lname;
    }

    // ---------------------------------------------------------- Public Methods
    /**
     * <p>Validates the user.  If the user doesn't exist or the password
     * is incorrect, the appropriate message is added to the current
     * <code>FacesContext</code>.  If the user successfully authenticates,
     * navigate them to the page referenced by the outcome <code>app-main</code>.
     * </p>
     *
     * @return <code>app-main</code> if the user authenticates, otherwise
     *  returns <code>null</code>
     */
    public String validateUser() {
        FacesContext context = FacesContext.getCurrentInstance();
        Player user = getUser();
        if (user != null) {
            if (!user.getPassword().equals(password)) {
                FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
                        "Login Failed!",
                        "The password specified is not correct.");
                context.addMessage(null, message);
                return null;
            }

            if (player == null) {
                player = new Game.bc.Player(username);
                this.playerName = username;
                System.out.println("playername changed:" + playerName);
            } else if (!(player.getName().equals(username))) {
                player = new Game.bc.Player(username);
                this.playerName = username;
                System.out.println("playername changed:" + playerName);
            } else {
                System.out.println("playername NOT changed:" + playerName);
            }

            context.getExternalContext().getSessionMap().put(USER_SESSION_KEY, user);
            return "app-main";
        } else {
            FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
                    "Login Failed!",
                    "Username '"
                    + username
                    + "' does not exist.");
            context.addMessage(null, message);
            return null;
        }
    }

    /**
     * <p>Creates a new <code>Player</code>.  If the specified user name exists
     * or an error occurs when persisting the Player instance, enqueue a message
     * detailing the problem to the <code>FacesContext</code>.  If the 
     * user is created, move the user back to the login view.</p>
     *
     * @return <code>login</code> if the user is created, otherwise
     *  returns <code>null</code>
     */
    public String createUser() {
        FacesContext context = FacesContext.getCurrentInstance();
        Player wuser = getUser();
        if (wuser == null) {
            if (!password.equals(passwordv)) {
                FacesMessage message = new FacesMessage("The specified passwords do not match.  Please try again");
                context.addMessage(null, message);
                return null;
            }
            wuser = new Player();
            wuser.setFirstname(fname);
            wuser.setLastname(lname);
            wuser.setPassword(password);
            wuser.setUsername(username);
            wuser.setSince(new Date());
            try {
                utx.begin();
                em.persist(wuser);
                utx.commit();
                return "login";
            } catch (Exception e) {
                FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
                        "Error creating user!",
                        "Unexpected error when creating your account.  Please contact the system Administrator");
                context.addMessage(null, message);
                Logger.getAnonymousLogger().log(Level.SEVERE,
                        "Unable to create new user",
                        e);
                return null;
            }
        } else {
            FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
                    "Username '"
                    + username
                    + "' already exists!  ",
                    "Please choose a different username.");
            context.addMessage(null, message);
            return null;
        }
    }

    /**
     * <p>When invoked, it will invalidate the user's session
     * and move them to the login view.</p>
     *
     * @return <code>login</code>
     */
    public String logout() {
        HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
        if (session != null) {
            session.invalidate();
        }
        return "login";

    }

    // --------------------------------------------------------- Private Methods
    /**
     * <p>This will attempt to lookup a <code>Player</code> object
     * based on the provided user name.</p>
     *
     * @return a <code>Player</code> object associated with the current
     *  username, otherwise, if no <code>Player</code> can be found,
     *  returns <code>null</code>
     */
    private Player getUser() {
        System.out.println("********User :" + username);
        try {
            Player user = (Player) em.createNamedQuery("Player.findByUsername").
                    setParameter("username", username).getSingleResult();
            System.out.println("********User Retrived:" + user.getFirstname() + "ln: " + user.getLastname() + "un:" + user.getUsername());
            return user;
        } catch (NoResultException nre) {
            System.out.println("********No User named:" + username);
            return null;
        }
    }
}
